package app

import (
	"context"
	"fmt"
	"gitee.com/eqyun/gosdk/config"
	"gitee.com/eqyun/gosdk/sdk"
	"gitee.com/eqyun/gosdk/sdk/common/middleware"

	"gitee.com/eqyun/gosdk/sdk/common/middleware/handler"
	"gitee.com/eqyun/gosdk/sdk/config/source/file"
	"gitee.com/eqyun/gosdk/sdk/core/database"
	"gitee.com/eqyun/gosdk/sdk/core/storage"
	"gitee.com/eqyun/gosdk/sdk/pkg"
	"github.com/gin-gonic/gin"
	"github.com/spf13/cobra"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path"
	"time"
)

var AppRouters = make([]func(), 0)

var (
	appName      string
	configYml    string
	makeDb       bool
	startLottery bool
	StartCmd     = &cobra.Command{
		Use:     "server",
		Short:   "Start API server",
		Example: "xxx -c config/settings.yml",
		PreRun: func(cmd *cobra.Command, args []string) {
			setup()
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			return run()
		},
	}
)

func init() {
	StartCmd.PersistentFlags().StringVarP(&appName, "name", "n", "", "Start server with provided configuration file")
	StartCmd.PersistentFlags().StringVarP(&configYml, "config", "c", "config/settings.yml", "Start server with provided configuration file")
	//AppRouters = append(AppRouters, router.InitUserRouter, adminRouter.InitAdminRouter)
}

func TestSetup() {
	setup()
}

func TestRun() {
	setup()
	run()
}
func setup() {
	//1. 读取配置
	config.Setup(
		file.NewSource(file.WithPath(configYml)),
		database.Setup, //初始化数据库
		storage.Setup,  //初始化缓存
	)
	usageStr := `starting api server...`
	log.Println(usageStr)
}

func run() error {
	//开始router
	//startLotteryEngine()
	initRouter()
	for _, f := range AppRouters {
		f()
	}

	srv := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", config.ApplicationConfig.Host, config.ApplicationConfig.Port),
		Handler: sdk.Runtime.GetEngine(),
	}

	go func() {
		// 服务连接
		if config.SslConfig.Enable {
			if err := srv.ListenAndServeTLS(config.SslConfig.Pem, config.SslConfig.KeyStr); err != nil && err != http.ErrServerClosed {
				log.Fatal("listen: ", err)
			}
		} else {
			if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
				log.Fatal("listen: ", err)
			}
		}
	}()
	fmt.Println(pkg.Green("web run at:"))
	fmt.Printf("-  Local:   http://localhost:%d/ \r\n", config.ApplicationConfig.Port)
	fmt.Printf("-  Network: http://%s:%d/ \r\n", pkg.GetLocaHonst(), config.ApplicationConfig.Port)

	fmt.Println(pkg.Green("Swagger run at:"))
	fmt.Printf("-  Local:   http://localhost:%d/swagger/admin/index.html \r\n", config.ApplicationConfig.Port)
	fmt.Printf("-  Network: http://%s:%d/swagger/admin/index.html \r\n", pkg.GetLocaHonst(), config.ApplicationConfig.Port)
	fmt.Printf("%s Enter Control + C Shutdown Server \r\n", pkg.GetCurrentTimeStr())
	// 等待中断信号以优雅地关闭服务器（设置 5 秒的超时时间）
	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt)
	<-quit

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	fmt.Printf("%s Shutdown Server ... \r\n", pkg.GetCurrentTimeStr())

	if err := srv.Shutdown(ctx); err != nil {
		log.Fatal("Server Shutdown:", err)
	}
	log.Println("Server exiting")

	return nil
}

func setStaticHtml(r *gin.Engine, hp, hv string) {
	r.GET(hv+"/*any", func(ctx *gin.Context) {
		if ctx.Request.Method != http.MethodGet {
			ctx.AbortWithStatus(http.StatusMethodNotAllowed)
			return
		}
		fmt.Println("load", hv, hp)
		ctx.HTML(http.StatusOK, path.Base(hp), gin.H{})
	})
}

func initRouter() {
	var r *gin.Engine
	h := sdk.Runtime.GetEngine()
	if h == nil {
		h = gin.New()
		sdk.Runtime.SetEngine(h)
	}
	switch h.(type) {
	case *gin.Engine:
		r = h.(*gin.Engine)
	default:
		log.Fatal("not support other engine")
		os.Exit(-1)
	}

	if config.WebConfig.StaticHtml != nil && len(config.WebConfig.StaticHtml) > 0 {
		r.LoadHTMLGlob("web/dist/*.html")

		for hv, hp := range config.WebConfig.StaticHtml {
			setStaticHtml(r, hp, hv)
		}
	}
	if config.WebConfig.StaticPath != nil && len(config.WebConfig.StaticPath) > 0 {
		for k, v := range config.WebConfig.StaticPath {
			r.Static(k, "web/"+v)
		}

	}

	if config.SslConfig.Enable {
		r.Use(handler.TlsHandler())
	}
	//r.Use(middleware.Metrics())
	r.Use(middleware.Sentinel()).
		Use(middleware.RequestId(pkg.TrafficKey))

	//Use(api.SetRequestLogger)

	middleware.InitMiddleware(r)
}
